#pragma once
#include <pthread.h>
#include <vector>
#include <sstream>
#include <unordered_map>
#include "Socket.hpp"

#define SIZE 10240
const string webroot = "./wwwroot";  // web根目录
const string homepage("index.html"); // 浏览器默认访问的html文件

struct ThreadData
{
    ThreadData(int sockfd, HttpServer* svr)
        : _sockfd(sockfd)
        ,_svr(svr)
    {
    }
    int _sockfd;
    HttpServer* _svr;
};

class HttpRequest // 处理客户端发来的报文
{
public:
    string Deserialize(string req) // 将请求报头储存在req_head中
    {
        while (true)
        {
            size_t pos = req.find("\r\n");
            if (string::npos == pos) // 说明发来的请求报文是空的，直接退出循环
                break;
            string tmp = req.substr(0, pos);
            if (tmp.empty()) // 判断http协议规定的 请求报头 和 请求正文之间的"\r\n"
                break;
            req_head.push_back(tmp);
            req.erase(0, pos + strlen("\r\n"));
        }
        req_text = req;
    }
    void Parse() // 提取出请求报头中的信息
    {
        // 将空格隔开的字符串进行分离
        stringstream s(req_head[0]);

        string Method, file_path, http_version;
        s >> Method >> file_path >> http_version; // file_path == a/b/junp.html
        if ("/" == file_path || "/index.html" == file_path)
        {
            file_path += webroot;
            file_path += "/";
            file_path += "index.html"; // file_path == ./wwwroot/index.html
        }
        else
        {
            file_path = webroot+file_path; // file_path == ./wwwroot/a/b/jump.html
        }
        size_t pos = file_path.rfind(".");
        suffix = file_path.substr(pos);
    }

public:
    string file_path; //客户端想要访问的文件数据
    vector<string> req_head; // 请求报头(http协议中的报头数据都是key,val形式)
    string suffix;           // 数据类型后缀
    string req_text;         // 请求正文
};

class HttpServer
{
public:
    HttpServer(const uint16_t &serverport)
        : _serverport(serverport), st(new Sock)
    {
        content_type.insert({".html", "text/html"});
        content_type.insert({".png", "image/png"});
    }
    ~HttpServer()
    {
        delete st;
    }

public:
    void Init()
    {
        st->Socket();
        st->Bind(_serverport);
        st->Listen();
    }
    void Start()
    {
        for (;;)
        {
            string clientip;
            uint16_t clientport;
            int sockfd = st->Accept(clientip, clientport);
            if (sockfd < 0)
            {
                continue; // 如果服务端获取连接失败，重新获取连接
            }

            // 创建新线程执行向客户端发送响应的任务
            pthread_t tid;
            ThreadData *td = new ThreadData(sockfd,this); // 在主进程的堆上创建一个ThreadData对象，主进程的堆区数据被新线程共享
            pthread_create(&tid, nullptr, ThreadRun, td);
        }
    }

public:
    // static Res Request(inbuffer)
    string SuffixToDesc(const string &suffix) // 获得响应报头中的content_type
    {
        auto iter = content_type.find(suffix);
        if (iter == content_type.end())
        {
            return content_type[".html"];
        }
        return content_type[suffix]; // 返回文件后缀.xxxx对应的content_type(响应报头，最终被浏览器识别)
    }

    static string ReadHtmlContent(const string& file_path)//
    {}

    void HandlerHttp(int sockfd) // 获取解析请求并且发送响应，构建遵守http协议的响应
    {
        char inbuffer[SIZE];
        ssize_t n = recv(sockfd, inbuffer, sizeof(inbuffer) - 1, 0); // 与系统调用read类似，返回值类型ssize_t
        if (recv > 0)
        {
            inbuffer[n] = 0;
            cout << inbuffer << endl;
            HttpRequest req;
            req.Deserialize(inbuffer);
            req.Parse();

            string text = ReadHtmlContent(req.file_path);
            bool judge = true;
            //string response_line = "HTTP /1.0 200 0K/r/n";  
            if(text.empty())
            {
                judge = false;
                string err_pathfile = webroot+"/";
                err_pathfile+="err.html";
                text = ReadHtmlContent(err_pathfile);
            }
            string response_line = "HTTP /1.0 200 0K\r\n";
            if(!judge)
            {
                response_line = "HTTP /1.0 404 Not Found\r\n";
            }
            string response_head = "Content-Length: " + to_string(text.size()); // 响应报头  Content-Length提示客户端浏览器该响应的报文长度
            response_head+="\r\n";
            response_head += "Content-Type: ";
            response_head += SuffixToDesc(req.suffix);
            response_head+="\r\n";
            string response = response_line + response_head + "\r\n" + text;
            //http协议请求和响应的格式是条信息最后都有"\r\n"
            send(sockfd,response.c_str(),response.size(),0);
        }
        close(sockfd);//主线程中创建的新线程共用主线程的文件描述符表！因此每次一个新线程完成通信任务后，要关闭对应的fd,以免导致主进程的文件描述符表中的资源被占用
    }
    static void *ThreadRun(void *args) // 或者将这个线程启动函数写在类外，就不会有参数this指针的影响了
    {                                  //被static修饰的函数参数列表没有this指针
        pthread_detach(pthread_self());
        ThreadData *td = static_cast<ThreadData *>(args);
        td->_svr->HandlerHttp(td->_sockfd);
        delete td;
        return nullptr;
    }

private:
    uint16_t _serverport;
    Sock *st;
    unordered_map<string, string> content_type;
};